From 89ff35083af68d1b24c1633886202ecc153af67d Mon Sep 17 00:00:00 2001
From: Angel Pons <th3fanbus@gmail.com>
Date: Sat, 7 May 2022 21:24:50 +0200
Subject: [PATCH 15/26] haswell NRI: Program memory map

This is very similar to Sandy/Ivy Bridge, except that there's several
registers to program in GDXCBAR. One of these GDXCBAR registers has a
lock bit that must be set in order for the memory controller to allow
normal access to DRAM. And it took me four months to realize this one
bit was the only reason why native raminit did not work.

Change-Id: I3af73a018a7ba948701a542e661e7fefd57591fe
Signed-off-by: Angel Pons <th3fanbus@gmail.com>
---
 .../intel/haswell/native_raminit/Makefile.inc |   1 +
 .../intel/haswell/native_raminit/memory_map.c | 183 ++++++++++++++++++
 .../haswell/native_raminit/raminit_main.c     |   1 +
 .../haswell/native_raminit/raminit_native.h   |   1 +
 .../intel/haswell/registers/host_bridge.h     |   2 +
 5 files changed, 188 insertions(+)
 create mode 100644 src/northbridge/intel/haswell/native_raminit/memory_map.c

diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.inc b/src/northbridge/intel/haswell/native_raminit/Makefile.inc
index fc55277a65..37d527e972 100644
--- a/src/northbridge/intel/haswell/native_raminit/Makefile.inc
+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.inc
@@ -4,6 +4,7 @@ romstage-y += configure_mc.c
 romstage-y += lookup_timings.c
 romstage-y += init_mpll.c
 romstage-y += io_comp_control.c
+romstage-y += memory_map.c
 romstage-y += raminit_main.c
 romstage-y += raminit_native.c
 romstage-y += spd_bitmunching.c
diff --git a/src/northbridge/intel/haswell/native_raminit/memory_map.c b/src/northbridge/intel/haswell/native_raminit/memory_map.c
new file mode 100644
index 0000000000..e3aded2b37
--- /dev/null
+++ b/src/northbridge/intel/haswell/native_raminit/memory_map.c
@@ -0,0 +1,183 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <device/pci_ops.h>
+#include <northbridge/intel/haswell/haswell.h>
+#include <southbridge/intel/lynxpoint/me.h>
+#include <types.h>
+
+#include "raminit_native.h"
+
+/* GDXCBAR */
+#define MPCOHTRK_GDXC_MOT_ADDRESS_LO		0x10
+#define MPCOHTRK_GDXC_MOT_ADDRESS_HI		0x14
+#define MPCOHTRK_GDXC_MOT_REGION		0x18
+
+#define MPCOHTRK_GDXC_OCLA_ADDRESS_LO		0x20
+#define MPCOHTRK_GDXC_OCLA_ADDRESS_HI		0x24
+#define MPCOHTRK_GDXC_OCLA_REGION		0x28
+
+/* This lock bit made me lose what little sanity I had left. - Angel Pons */
+#define MPCOHTRK_GDXC_OCLA_ADDRESS_HI_LOCK	BIT(2)
+
+static inline uint32_t gdxcbar_read32(const uintptr_t offset)
+{
+	return read32p((mchbar_read32(GDXCBAR) & ~1) + offset);
+}
+
+static inline void gdxcbar_write32(const uintptr_t offset, const uint32_t value)
+{
+	write32p((mchbar_read32(GDXCBAR) & ~1) + offset, value);
+}
+
+static inline void gdxcbar_clrsetbits32(const uintptr_t offset, uint32_t clear, uint32_t set)
+{
+	const uintptr_t address = (mchbar_read32(GDXCBAR) & ~1) + offset;
+	clrsetbits32((void *)address, clear, set);
+}
+
+#define gdxcbar_setbits32(offset, set)		gdxcbar_clrsetbits32(offset, 0, set)
+#define gdxcbar_clrbits32(offset, clear)	gdxcbar_clrsetbits32(offset, clear, 0)
+
+/* All values stored in here (except the bool) are specified in MiB */
+struct memory_map_data {
+	uint32_t dpr_size;
+	uint32_t tseg_size;
+	uint32_t gtt_size;
+	uint32_t gms_size;
+	uint32_t me_stolen_size;
+	uint32_t mmio_size;
+	uint32_t touud;
+	uint32_t remaplimit;
+	uint32_t remapbase;
+	uint32_t tom;
+	uint32_t tom_minus_me;
+	uint32_t tolud;
+	uint32_t bdsm_base;
+	uint32_t gtt_base;
+	uint32_t tseg_base;
+	bool reclaim_possible;
+};
+
+static void compute_memory_map(struct memory_map_data *map)
+{
+	map->tom_minus_me = map->tom - map->me_stolen_size;
+
+	/*
+	 * MMIO size will actually be slightly smaller than computed,
+	 * but matches what MRC does and is more MTRR-friendly given
+	 * that TSEG is treated as WB, but SMRR makes TSEG UC anyway.
+	 */
+	const uint32_t mmio_size = MIN(map->tom_minus_me, 4096) / 2;
+	map->gtt_base = ALIGN_DOWN(mmio_size, map->tseg_size);
+	map->tseg_base = map->gtt_base - map->tseg_size;
+	map->bdsm_base = map->gtt_base + map->gtt_size;
+	map->tolud = map->bdsm_base + map->gms_size;
+	map->reclaim_possible = map->tom_minus_me > map->tolud;
+
+	if (map->reclaim_possible) {
+		map->remapbase  = MAX(4096, map->tom_minus_me);
+		map->touud      = MIN(4096, map->tom_minus_me) + map->remapbase - map->tolud;
+		map->remaplimit = map->touud - 1;
+	} else {
+		map->remapbase  = 0;
+		map->remaplimit = 0;
+		map->touud = map->tom_minus_me;
+	}
+}
+
+static void display_memory_map(const struct memory_map_data *map)
+{
+	if (!CONFIG(DEBUG_RAM_SETUP))
+		return;
+
+	printk(BIOS_DEBUG, "============ MEMORY MAP ============\n");
+	printk(BIOS_DEBUG, "\n");
+	printk(BIOS_DEBUG, "dpr_size       = %u MiB\n", map->dpr_size);
+	printk(BIOS_DEBUG, "tseg_size      = %u MiB\n", map->tseg_size);
+	printk(BIOS_DEBUG, "gtt_size       = %u MiB\n", map->gtt_size);
+	printk(BIOS_DEBUG, "gms_size       = %u MiB\n", map->gms_size);
+	printk(BIOS_DEBUG, "me_stolen_size = %u MiB\n", map->me_stolen_size);
+	printk(BIOS_DEBUG, "\n");
+	printk(BIOS_DEBUG, "touud          = %u MiB\n", map->touud);
+	printk(BIOS_DEBUG, "remaplimit     = %u MiB\n", map->remaplimit);
+	printk(BIOS_DEBUG, "remapbase      = %u MiB\n", map->remapbase);
+	printk(BIOS_DEBUG, "tom            = %u MiB\n", map->tom);
+	printk(BIOS_DEBUG, "tom_minus_me   = %u MiB\n", map->tom_minus_me);
+	printk(BIOS_DEBUG, "tolud          = %u MiB\n", map->tolud);
+	printk(BIOS_DEBUG, "bdsm_base      = %u MiB\n", map->bdsm_base);
+	printk(BIOS_DEBUG, "gtt_base       = %u MiB\n", map->gtt_base);
+	printk(BIOS_DEBUG, "tseg_base      = %u MiB\n", map->tseg_base);
+	printk(BIOS_DEBUG, "\n");
+	printk(BIOS_DEBUG, "reclaim_possible = %s\n", map->reclaim_possible ? "Yes" : "No");
+}
+
+static void map_write_reg64(const uint16_t reg, const uint64_t size)
+{
+	const uint64_t value = size << 20;
+	pci_write_config32(HOST_BRIDGE, reg + 4, value >> 32);
+	pci_write_config32(HOST_BRIDGE, reg + 0, value >>  0);
+}
+
+static void map_write_reg32(const uint16_t reg, const uint32_t size)
+{
+	const uint32_t value = size << 20;
+	pci_write_config32(HOST_BRIDGE, reg, value);
+}
+
+static void program_memory_map(const struct memory_map_data *map)
+{
+	map_write_reg64(TOUUD, map->touud);
+	map_write_reg64(TOM,   map->tom);
+	if (map->reclaim_possible) {
+		map_write_reg64(REMAPBASE,  map->remapbase);
+		map_write_reg64(REMAPLIMIT, map->remaplimit);
+	}
+	if (map->me_stolen_size) {
+		map_write_reg64(MESEG_LIMIT, 0x80000 - map->me_stolen_size);
+		map_write_reg64(MESEG_BASE, map->tom_minus_me);
+		pci_or_config32(HOST_BRIDGE, MESEG_LIMIT, ME_STLEN_EN);
+	}
+	map_write_reg32(TOLUD, map->tolud);
+	map_write_reg32(BDSM,  map->bdsm_base);
+	map_write_reg32(BGSM,  map->gtt_base);
+	map_write_reg32(TSEG,  map->tseg_base);
+
+	const uint32_t dpr_reg = map->tseg_base << 20 | map->dpr_size << 4;
+	pci_write_config32(HOST_BRIDGE, DPR, dpr_reg);
+
+	const uint16_t gfx_stolen_size = GGC_IGD_MEM_IN_32MB_UNITS(map->gms_size / 32);
+	const uint16_t ggc = map->gtt_size << 8 | gfx_stolen_size;
+	pci_write_config16(HOST_BRIDGE, GGC, ggc);
+
+	/** TODO: Do not hardcode these? GDXC has weird alignment requirements, though. **/
+	gdxcbar_write32(MPCOHTRK_GDXC_MOT_ADDRESS_LO, 0);
+	gdxcbar_write32(MPCOHTRK_GDXC_MOT_ADDRESS_HI, 0);
+	gdxcbar_write32(MPCOHTRK_GDXC_MOT_REGION, 0);
+
+	gdxcbar_write32(MPCOHTRK_GDXC_OCLA_ADDRESS_LO, 0);
+	gdxcbar_write32(MPCOHTRK_GDXC_OCLA_ADDRESS_HI, 0);
+	gdxcbar_write32(MPCOHTRK_GDXC_OCLA_REGION, 0);
+
+	gdxcbar_setbits32(MPCOHTRK_GDXC_OCLA_ADDRESS_HI, MPCOHTRK_GDXC_OCLA_ADDRESS_HI_LOCK);
+}
+
+enum raminit_status configure_memory_map(struct sysinfo *ctrl)
+{
+	struct memory_map_data memory_map = {
+		.tom            = ctrl->channel_size_mb[0] + ctrl->channel_size_mb[1],
+		.dpr_size       = CONFIG_INTEL_TXT_DPR_SIZE,
+		.tseg_size      = CONFIG_SMM_TSEG_SIZE >> 20,
+		.me_stolen_size = intel_early_me_uma_size(),
+	};
+	/** FIXME: MRC hardcodes iGPU parameters, but we should not **/
+	const bool igpu_on = pci_read_config32(HOST_BRIDGE, DEVEN) & DEVEN_D2EN;
+	if (CONFIG(ONBOARD_VGA_IS_PRIMARY) || igpu_on) {
+		memory_map.gtt_size = 2;
+		memory_map.gms_size = 64;
+		pci_or_config32(HOST_BRIDGE, DEVEN, DEVEN_D2EN);
+	}
+	compute_memory_map(&memory_map);
+	display_memory_map(&memory_map);
+	program_memory_map(&memory_map);
+	return 0;
+}
diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c
index 3a773cfa19..136a8ba989 100644
--- a/src/northbridge/intel/haswell/native_raminit/raminit_main.c
+++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.c
@@ -24,6 +24,7 @@ static const struct task_entry cold_boot[] = {
 	{ initialise_mpll,                                        true, "INITMPLL",   },
 	{ convert_timings,                                        true, "CONVTIM",    },
 	{ configure_mc,                                           true, "CONFMC",     },
+	{ configure_memory_map,                                   true, "MEMMAP",     },
 };
 
 /* Return a generic stepping value to make stepping checks simpler */
diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
index cd1f2eb2a5..4763b25e8d 100644
--- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h
+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
@@ -202,6 +202,7 @@ enum raminit_status collect_spd_info(struct sysinfo *ctrl);
 enum raminit_status initialise_mpll(struct sysinfo *ctrl);
 enum raminit_status convert_timings(struct sysinfo *ctrl);
 enum raminit_status configure_mc(struct sysinfo *ctrl);
+enum raminit_status configure_memory_map(struct sysinfo *ctrl);
 
 void configure_timings(struct sysinfo *ctrl);
 void configure_refresh(struct sysinfo *ctrl);
diff --git a/src/northbridge/intel/haswell/registers/host_bridge.h b/src/northbridge/intel/haswell/registers/host_bridge.h
index 1ee0ab2890..0228cf6bb9 100644
--- a/src/northbridge/intel/haswell/registers/host_bridge.h
+++ b/src/northbridge/intel/haswell/registers/host_bridge.h
@@ -34,6 +34,8 @@
 
 #define MESEG_BASE	0x70	/* Management Engine Base */
 #define MESEG_LIMIT	0x78	/* Management Engine Limit */
+#define  MELCK		(1 << 10)	/* ME Range Lock */
+#define  ME_STLEN_EN	(1 << 11)	/* ME Stolen Memory Enable */
 
 #define PAM0		0x80
 #define PAM1		0x81
-- 
2.39.2

